package com.example.paymentdemo.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.example.paymentdemo.entity.OrderInfo;
import com.example.paymentdemo.entity.RefundInfo;
import com.example.paymentdemo.enums.OrderStatus;
import com.example.paymentdemo.enums.PayType;
import com.example.paymentdemo.enums.wxpay.AliPayTradeState;
import com.example.paymentdemo.service.AliPayService;
import com.example.paymentdemo.service.OrderInfoService;
import com.example.paymentdemo.service.PaymentInfoService;
import com.example.paymentdemo.service.RefundInfoService;
import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Service
public class AliPayServiceImpl implements AliPayService {
    @Autowired
    private Environment environment;

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private OrderInfoService orderInfoService;

    @Autowired
    private AlipayClient alipayClient;

    @Autowired
    private RefundInfoService refundsInfoService;

    //锁
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public String tradeCreate(Long productId) throws AlipayApiException {
        log.info("生成订单");

        //生成订单
        OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId, PayType.ALIPAY.getType());

        //调用支付宝接口发送请求

        //1.请求
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        //2.设置业务数据
        AlipayTradePagePayModel bizModel = new AlipayTradePagePayModel();
        // 商品订单号 为订单id
        bizModel.setOutTradeNo(orderInfo.getOrderNo());
        //单位是元
        bizModel.setTotalAmount(orderInfo.getTotalFee().toString());
        // 订单标题
        bizModel.setSubject(orderInfo.getTitle());
        //默认的
        bizModel.setProductCode("FAST_INSTANT_TRADE_PAY");
        //3.绑定
        request.setBizModel(bizModel);
        // 支付成功后返回哪里
        request.setReturnUrl(environment.getProperty("alipay.return-url"));
        // 结果回调地址
        request.setNotifyUrl(environment.getProperty("alipay.notify-url"));
        //用户支付后支付宝会以GET方法请求returnUrl,并且携带out_trade_no,trade_no,total_amount等参数.
        AlipayTradePagePayResponse response = null;
        try {
            //完成签名并执行请求
            response = alipayClient.pageExecute(request);
            if (response.isSuccess()) {
                log.debug("调用成功,参数为===>{}", JSON.toJSONString(response.getBody()));
                return response.getBody();
            } else {
                log.error("调用失败");
                log.error(response.getMsg());
                return null;
            }
        } catch (AlipayApiException e) {
            log.error("调用异常");
            return null;
        }
    }

    /**
     * 处理订单 修改订单信息  记录支付信息
     *
     * @param params
     */
    @Transactional(rollbackFor = Exception.class)//多表操作启用事务
    @Override
    public void processOrder(Map<String, String> params) {
        log.info("处理订单");

        //获取订单号
        String orderNo = params.get("out_trade_no");
        //TODO 锁加速可以用分布式锁 降粒度处理
        if (lock.tryLock()) {
            try {

                //重复调用幂等性  如果不是未支付就跳过
                String orderStatus = orderInfoService.getOrderStatus(orderNo);

                if (!OrderStatus.NOTPAY.getType().equals(orderStatus)) {
                    log.info("订单已支付");
                    return;
                }


                //更新订单状态
                orderInfoService.updateStatusByOrderNo(orderNo, OrderStatus.SUCCESS);

                //记录支付信息
                paymentInfoService.createPaymentInfoForAliPay(params);

            } finally {
                lock.unlock();
            }
        }

    }

    /**
     * 取消订单
     * @param orderNo
     */
    @Override
    public void cancelOrder(String orderNo) throws AlipayApiException {

        closeOrder(orderNo);

        //更新订单状态
        orderInfoService.updateStatusByOrderNo(orderNo, OrderStatus.CANCEL);
    }

    /**
     * 查询订单
     * @param orderNo
     * @return
     */
    @Override
    public String queryOrder(String orderNo) throws AlipayApiException {
        log.info("查询订单");

        // 构造请求参数以调用接口
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        AlipayTradeQueryModel model = new AlipayTradeQueryModel();

        // 设置订单支付时传入的商户订单号
        model.setOutTradeNo("orderNo");

        request.setBizModel(model);


        AlipayTradeQueryResponse response = alipayClient.execute(request);
        System.out.println(response.getBody());

        if (response.isSuccess()) {
            log.debug("调用成功,参数为===>{}", JSON.toJSONString(response.getBody()));
            return response.getBody();
        } else {
            log.error("调用失败");
            log.error(response.getMsg());
            return null;//表示的是订单不存在
        }
    }

    /**
     * 校验本地未支付订单  只有三种状态 1.未支付 2.已支付 3.已关闭（也就是远程没订单）
     * @param orderNo
     */
    @Override
    public void checkOrderStatus(String orderNo) throws AlipayApiException {
        log.warn("根据订单号核实订单状态 ===> {}", orderNo);
        String result = this.queryOrder(orderNo);
        //订单未创建
        if(result == null){
            log.warn("核实订单未创建 ===> {}", orderNo);
        //更新本地订单状态
            orderInfoService.updateStatusByOrderNo(orderNo, OrderStatus.CLOSED);
        }
        //解析查单响应结果
        Gson gson = new Gson();
        HashMap<String, LinkedTreeMap> resultMap = gson.fromJson(result, HashMap.class);
        LinkedTreeMap alipayTradeQueryResponse =
                resultMap.get("alipay_trade_query_response");
        String tradeStatus = (String)alipayTradeQueryResponse.get("trade_status");
        if(AliPayTradeState.NOTPAY.getType().equals(tradeStatus)){
            log.warn("核实订单未支付 ===> {}", orderNo);
            //如果订单未支付，则调用关单接口关闭订单
            this.closeOrder(orderNo);
            // 并更新商户端订单状态
            orderInfoService.updateStatusByOrderNo(orderNo, OrderStatus.CLOSED);
        }
        if(AliPayTradeState.SUCCESS.getType().equals(tradeStatus)){
            log.warn("核实订单已支付 ===> {}", orderNo);
            //如果订单已支付，则更新商户端订单状态
            orderInfoService.updateStatusByOrderNo(orderNo, OrderStatus.SUCCESS);
            //并记录支付日志
            paymentInfoService.createPaymentInfoForAliPay(alipayTradeQueryResponse);
        }
    }

    /**
     * 退款
     * @param orderNo
     * @param reason
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void refund(String orderNo, String reason) {
        try {
            log.info("调用退款API");
            //创建退款单
            RefundInfo refundInfo =
                    refundsInfoService.createRefundByOrderNoForAliPay(orderNo, reason);
            //调用统一收单交易退款接口
            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
            //组装当前业务方法的请求参数
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderNo);//订单编号
            BigDecimal refund = new
                    BigDecimal(refundInfo.getRefund().toString()).divide(new BigDecimal("100"));
            //BigDecimal refund = new BigDecimal("2").divide(new BigDecimal("100"));
            bizContent.put("refund_amount", refund);//退款金额：不能大于支付金额
            bizContent.put("refund_reason", reason);//退款原因(可选)
            request.setBizContent(bizContent.toString());
            //执行请求，调用支付宝接口
            AlipayTradeRefundResponse response = alipayClient.execute(request);
            if(response.isSuccess()){
                log.info("调用成功，返回结果 ===> " + response.getBody());
                //更新订单状态
                orderInfoService.updateStatusByOrderNo(orderNo,
                        OrderStatus.REFUND_SUCCESS);
                //更新退款单
                refundsInfoService.updateRefundForAliPay(
                        refundInfo.getRefundNo(),
                        response.getBody(),
                        AliPayTradeState.REFUND_SUCCESS.getType()); //退款成功
            } else {
                log.info("调用失败，返回码 ===> " + response.getCode() + ", 返回描述 ===> "
                        + response.getMsg());
                //更新订单状态
                orderInfoService.updateStatusByOrderNo(orderNo,
                        OrderStatus.REFUND_ABNORMAL);
                //更新退款单
                refundsInfoService.updateRefundForAliPay(
                        refundInfo.getRefundNo(),
                        response.getBody(),
                        AliPayTradeState.REFUND_ERROR.getType()); //退款失败
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("创建退款申请失败");

        }
    }

    /**
     * 查询退款
     * @param orderNo
     * @return
     */
    @Override
    public String queryRefund(String orderNo) {
        try {
            log.info("查询退款接口调用 ===> {}", orderNo);
            AlipayTradeFastpayRefundQueryRequest request = new
                    AlipayTradeFastpayRefundQueryRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderNo);
            bizContent.put("out_request_no", orderNo);
            request.setBizContent(bizContent.toString());
            AlipayTradeFastpayRefundQueryResponse response =
                    alipayClient.execute(request);
            if(response.isSuccess()){
                log.info("调用成功，返回结果 ===> " + response.getBody());
                return response.getBody();
            } else {
                log.info("调用失败，返回码 ===> " + response.getCode() + ", 返回描述 ===> "
                        + response.getMsg());
//throw new RuntimeException("查单接口的调用失败");
                return null;//订单不存在
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("查单接口的调用失败");
        }
    }

    /**
     * 申请账单
     * @param billDate
     * @param type
     * @return
     */
    @Override
    public String queryBill(String billDate, String type) {
        try {
            AlipayDataDataserviceBillDownloadurlQueryRequest request = new
                    AlipayDataDataserviceBillDownloadurlQueryRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("bill_type", type);
            bizContent.put("bill_date", billDate);
            request.setBizContent(bizContent.toString());
            AlipayDataDataserviceBillDownloadurlQueryResponse response =
                    alipayClient.execute(request);
            if(response.isSuccess()){
                log.info("调用成功，返回结果 ===> " + response.getBody());
//获取账单下载地址
                Gson gson = new Gson();
                HashMap<String, LinkedTreeMap> resultMap =
                        gson.fromJson(response.getBody(), HashMap.class);
                LinkedTreeMap billDownloadurlResponse =
                        resultMap.get("alipay_data_dataservice_bill_downloadurl_query_response");
                String billDownloadUrl =
                        (String)billDownloadurlResponse.get("bill_download_url");
                return billDownloadUrl;
            } else {
                log.info("调用失败，返回码 ===> " + response.getCode() + ", 返回描述 ===> "
                        + response.getMsg());
                throw new RuntimeException("申请账单失败");
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("申请账单失败");
        }
    }


    /**
     * 调用支付宝统一收单交易关闭接口
     * @param orderNo
     */
    private void closeOrder(String orderNo) throws AlipayApiException {
        AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("trade_no", orderNo);
        request.setBizContent(bizContent.toString());
        AlipayTradeCloseResponse response = alipayClient.execute(request);
        if(response.isSuccess()){
            log.info("关单接口调用成功",response.getBody());
        } else {
            log.error("关单接口调用失败",response.getBody());
        }
    }

}
